package org.example.short_link.project.service.Impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.Week;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.example.short_link.project.common.convention.Exception.ClientException;
import org.example.short_link.project.common.convention.Exception.ServiceException;
import org.example.short_link.project.common.enums.VailDataTypeEnum;
import org.example.short_link.project.dao.entity.*;
import org.example.short_link.project.dao.mapper.*;
import org.example.short_link.project.dto.req.ShortLinkCreateReqDTO;
import org.example.short_link.project.dto.req.ShortLinkPageReqDTO;
import org.example.short_link.project.dto.req.ShortLinkUpdateReqDTO;
import org.example.short_link.project.dto.resp.ShortLinkCreateRespDTO;
import org.example.short_link.project.dto.resp.ShortLinkGroupCountQueryRespDTO;
import org.example.short_link.project.dto.resp.ShortLinkPageRespDTO;
import org.example.short_link.project.service.ShortLinkService;
import org.example.short_link.project.toolkit.HashUtil;
import org.example.short_link.project.toolkit.LinkUtill;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import static org.example.short_link.project.common.constant.RedisKeyConstant.*;
import static org.example.short_link.project.common.constant.ShortLinkConstant.AMP_REMOTE_URL;

@Service
@RequiredArgsConstructor
@Slf4j
public class ShortLinkServiceImpl extends ServiceImpl<ShortLinkMapper, ShortLinkDO> implements ShortLinkService {

    private final RBloomFilter<String> ShortURICachePenetrationBloomFilter;

    private final ShortLinkGoToMapper shortLinkGoToMapper;

    private final StringRedisTemplate stringRedisTemplate;

    private final RedissonClient redissonClient;

    private final LinkAccessStatsMapper linkAccessStatsMapper;

    private final LinkLocaleStatsMapper linkLocaleStatsMapper;

    private final LinkOsStatsMapper linkOsStatsMapper;

    private final LinkBrowserStatsMapper linkbrowserStatsMapper;

    private final LinkAccessLogsMapper linkAccessLogsMapper;

    private final LinkDeviceStatsMapper linkDeviceStatsMapper;

    private final LinkNetworkStatsMapper linkNetworkStatsMapper;

    @Value("${short-link.stats.locale.amap-key}")
    private String LocalAmpKey;

    @Value("${short-link.domain.default}")
    private String domain;


    @Override
    public ShortLinkCreateRespDTO createShortLink(ShortLinkCreateReqDTO requestParam) {
        String shortLinkSuffix = generateSuffix(requestParam);
        String fullShortUrl = StrBuilder.create(domain)
                .append("/")
                .append(shortLinkSuffix)
                .toString();
        //String fullShortUrl = requestParam.getDomain() + "/" + shortLinkSuffix;
//        ShortLinkDO shortLinkDO = BeanUtil.toBean(requestParam, ShortLinkDO.class);
//        shortLinkDO.setShortUri(shortLinkSuffix);
//        //拼接完整链接 域名+/+短链接关键字
//        shortLinkDO.setFullShortUrl(fullShortUrl);
//        shortLinkDO.setEnableStatus(0);
        ShortLinkDO shortLinkDO = ShortLinkDO.builder()
                .domain(domain)
                .originUrl(requestParam.getOriginUrl())
                .gid(requestParam.getGid())
                .createdType(requestParam.getCreatedType())
                .validDateType(requestParam.getValidDateType())
                .validDate(requestParam.getValidDate())
                .describe(requestParam.getDescribe())
                .shortUri(shortLinkSuffix)
                .enableStatus(0)
                .fullShortUrl(fullShortUrl)
                .favicon(getFavicon(requestParam.getOriginUrl()))
                .build();
        ShortLinkGoToDO linkGoToDO = ShortLinkGoToDO.builder()
                .fullShortUrl(fullShortUrl)
                .gid(requestParam.getGid())
                .build();
        try {
            baseMapper.insert(shortLinkDO);
            //在创建短链接的时候，还需要将gid，fullshortUrl插入到短链接路由表中 为什么？
            shortLinkGoToMapper.insert(linkGoToDO);
        }catch (DuplicateKeyException ex){
            //误判的前提是 布隆过滤器容量接近满了
            // TODO : 已经误判的短链接如何处理？ 查数据库解决
            // 第一种情况：短链接真实存在缓存中
            // 第二种情况：短链接不一定存在缓存中（机率很小）
            LambdaQueryWrapper<ShortLinkDO> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(ShortLinkDO::getFullShortUrl,fullShortUrl);
            ShortLinkDO hasShortLinkDO = baseMapper.selectOne(queryWrapper);
            if(hasShortLinkDO != null){ //如果数据库中真的存在该连接，那就是真的重复，而不是误判
                log.warn("短链接: {} 重复入库",fullShortUrl);
                throw new ServiceException("短链接生成重复");
            }
            //如果数据库里面没有 ， 那就是误判，然后往下走，把连接加入到布隆过滤器中

        }
        //创建短链接 根据传过来的有效期类型和有效期时间，设置缓存时间. 缓存预热!
        stringRedisTemplate.opsForValue().set(
                //注意key要保持一致
                String.format(GOTO_SHORT_LINK_KEY, fullShortUrl),
                requestParam.getOriginUrl(),
                LinkUtill.getLinkCacheValidTime(requestParam.getValidDate()),
                TimeUnit.MICROSECONDS
        );
        ShortURICachePenetrationBloomFilter.add(fullShortUrl);
        //然后就将数据返回出去
        return ShortLinkCreateRespDTO.builder()
                //http:// shortLinkDO.getFullShortUrl()：完整短链接
                .fullShortUrl("http://" + shortLinkDO.getFullShortUrl())
                .originUrl(requestParam.getOriginUrl())
                .gid(requestParam.getGid())
                .build();


    }


    @Override
    public IPage<ShortLinkPageRespDTO> pageShortLink(ShortLinkPageReqDTO requestParam) {

        LambdaQueryWrapper<ShortLinkDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ShortLinkDO::getGid,requestParam.getGid())
                .eq(ShortLinkDO::getEnableStatus,0)
                .eq(ShortLinkDO::getDelFlag , 0)
                .orderByDesc(ShortLinkDO::getCreateTime);
        // 将 `resultPage` 中的每个 `ShortLinkDO` 对象转换为 `ShortLinkPageRespDTO` 对象，
        // 并返回一个新的 `IPage<ShortLinkPageRespDTO>`
        IPage<ShortLinkDO> resultPage = baseMapper.selectPage(requestParam, queryWrapper);

        return resultPage.convert(each ->{

            ShortLinkPageRespDTO result = BeanUtil.toBean(each, ShortLinkPageRespDTO.class);
            result.setDomain("http://" + result.getDomain());
            return result;

        });

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateShortLink(ShortLinkUpdateReqDTO requestParam) {
        /**
         * 有一个需求：如果传递过来的短链接是需要更换分组的，
         * 那么我们下面的操作，就是非法操作了，因为我们下面的操作是基于gid，也就是当前分组
         */
        LambdaQueryWrapper<ShortLinkDO> queryWrapper = Wrappers.lambdaQuery(ShortLinkDO.class)
                .eq(ShortLinkDO::getFullShortUrl, requestParam.getFullShortUrl())
                .eq(ShortLinkDO::getDelFlag, 0)
                .eq(ShortLinkDO::getEnableStatus, 0)
                .eq(ShortLinkDO::getGid, requestParam.getGid());
        ShortLinkDO hasShortLinkDO = baseMapper.selectOne(queryWrapper);
        if(hasShortLinkDO == null){
            throw new ClientException("短链接记录不存在");
        }
            ShortLinkDO shortLinkDO = ShortLinkDO.builder()
                    .domain(hasShortLinkDO.getDomain())
                    .shortUri(hasShortLinkDO.getShortUri())
                    .clickNum(hasShortLinkDO.getClickNum())
                    .favicon(hasShortLinkDO.getFavicon())
                    .createdType(hasShortLinkDO.getCreatedType())
                    .gid(requestParam.getGid())
                    .originUrl(requestParam.getOriginUrl())
                    .describe(requestParam.getDescribe())
                    .validDateType(requestParam.getValidDateType())
                    .validDate(requestParam.getValidDate())
                    .build();
            if(Objects.equals(hasShortLinkDO.getGid(), requestParam.getGid())){
                LambdaUpdateWrapper<ShortLinkDO> updateWrapper = Wrappers.lambdaUpdate(ShortLinkDO.class)
                        .eq(ShortLinkDO::getFullShortUrl, requestParam.getFullShortUrl())
                        .eq(ShortLinkDO::getDelFlag, 0)
                        .eq(ShortLinkDO::getEnableStatus, 0)
                        .eq(ShortLinkDO::getGid, requestParam.getGid())
                        //意思是如果我下面条件成立(也就是短链接有效期为永久时，把值设置为null)
                        .set(Objects.equals(requestParam.getValidDateType(), VailDataTypeEnum.PERMANENT.getType()), ShortLinkDO::getValidDate, null);
                baseMapper.update(shortLinkDO,updateWrapper);
            }else{
                LambdaUpdateWrapper<ShortLinkDO> updateWrapper = Wrappers.lambdaUpdate(ShortLinkDO.class)
                        .eq(ShortLinkDO::getFullShortUrl,requestParam.getFullShortUrl())
                        .eq(ShortLinkDO::getGid,hasShortLinkDO.getGid())
                        .eq(ShortLinkDO::getDelFlag,0)
                        .eq(ShortLinkDO::getEnableStatus,0);
                baseMapper.delete(updateWrapper);
                shortLinkDO.setGid(requestParam.getGid());
                baseMapper.insert(shortLinkDO);
            }
    }

    @SneakyThrows
    @Override
    public void restoreUrl(String shorturi, HttpServletRequest request, HttpServletResponse response) {
        //获取域名
        String serverName = request.getServerName();
        //获取端口
        String serverPort = Optional.of(request.getServerPort())
                .filter(each -> !Objects.equals(each , 80))
                //如果说不等于80 也就是为true，执行以下操作
                .map(String::valueOf)
                .map(each -> ":" + each)
                //如果说等于80 也就是false的情况，执行下面操作
                .orElse("");

        String fullShortUrl = serverName + serverPort +"/" + shorturi;
        String originaLink = stringRedisTemplate.opsForValue().get(String.format(GOTO_SHORT_LINK_KEY, fullShortUrl));
        if(StrUtil.isNotBlank(originaLink)){
            shortLinkStatus(fullShortUrl , null, request,response);
            response.sendRedirect(originaLink);
            return;
        }
        //如果布隆过滤器中不存在fullShorturl，也返回
        boolean contains = ShortURICachePenetrationBloomFilter.contains(fullShortUrl);
        if(!contains){
            response.sendRedirect("/page/notfound");
            return;
        }
        //判断请求key是否存在空值，存在是返回空
        String gotoIsNullShortLink = stringRedisTemplate.opsForValue().get(String.format(GOTO_IS_NULL_SHORT_LINK_KEY, fullShortUrl));
        if(StrUtil.isNotBlank(gotoIsNullShortLink)){
            response.sendRedirect("/page/notfound");
            return;
        }

        RLock lock = redissonClient.getLock(String.format(LOCK_GOTO_SHORT_LINK_KEY, fullShortUrl));
        //加锁
        lock.lock();
        try {

            originaLink = stringRedisTemplate.opsForValue().get(String.format(GOTO_SHORT_LINK_KEY, fullShortUrl));
            //双重判定锁
            if(StrUtil.isNotBlank(originaLink)){
                shortLinkStatus(fullShortUrl , null,request,response);
                response.sendRedirect(originaLink);
                return;
            }
            LambdaQueryWrapper<ShortLinkGoToDO> shortLinkGoToDOLambdaQueryWrapper = Wrappers.lambdaQuery(ShortLinkGoToDO.class)
                    .eq(ShortLinkGoToDO::getFullShortUrl, fullShortUrl);

            ShortLinkGoToDO shortLinkGoToDO = shortLinkGoToMapper.selectOne(shortLinkGoToDOLambdaQueryWrapper);
            if(shortLinkGoToDO == null){ //跳转路由表为空 , 需要进行封控
                //如果路由中间表查询为空，则将key设置为空值，并返回
                stringRedisTemplate.opsForValue().set(String.format(LOCK_GOTO_SHORT_LINK_KEY, fullShortUrl),"-",30, TimeUnit.HOURS);
                response.sendRedirect("/page/notfound");
                return;
            }
            LambdaQueryWrapper<ShortLinkDO> queryWrapper = Wrappers.lambdaQuery(ShortLinkDO.class)
                    .eq(ShortLinkDO::getFullShortUrl,fullShortUrl)
                    .eq(ShortLinkDO::getDelFlag, 0)
                    .eq(ShortLinkDO::getEnableStatus, 0)
                    .eq(ShortLinkDO::getGid,shortLinkGoToDO.getGid());
            ShortLinkDO shortLinkDO = baseMapper.selectOne(queryWrapper);
            if(shortLinkDO == null || (shortLinkDO.getValidDate() != null && shortLinkDO.getValidDate().before(new Date()))) {
                //判断短链接过期的场景，如果当前短链接的有效期时间有值，但是时间在我们当前时间之前， 则表示过期,则将key在缓存中对应的数据设置为空
                //如果shortLinkDO不等于空，但是超出有效期了，表示短链接过期，将key对应的value值设置为空
                stringRedisTemplate.opsForValue().set(String.format(LOCK_GOTO_SHORT_LINK_KEY, fullShortUrl), "-", 30, TimeUnit.HOURS);
                return;
            }

                //设置缓存预热，第一次查的是 会不存在于缓存，所以在这里创建一个查询，查完就加入到缓存中
                stringRedisTemplate.opsForValue().set(
                        //注意key要保持一致
                        String.format(GOTO_SHORT_LINK_KEY, fullShortUrl),
                        shortLinkDO.getOriginUrl(),
                        LinkUtill.getLinkCacheValidTime(shortLinkDO.getValidDate()),
                        TimeUnit.MICROSECONDS
                );

                //调用短链接跳转前，记录基础服务
                shortLinkStatus(fullShortUrl , null ,request,response);
                //如果不等于空 ，就进行跳转 短链接 ——> 原链接
                response.sendRedirect(shortLinkDO.getOriginUrl());




        }finally {
            //解锁
            lock.unlock();
        }







    }

    private void shortLinkStatus(String fullShortUrl,String gid,HttpServletRequest request , HttpServletResponse response){
        AtomicBoolean uvFirstFlag = new AtomicBoolean();
        Cookie[] cookies = request.getCookies();
        boolean uipFirstFlag;



        try {

            AtomicReference<String> uv = new AtomicReference<>();

            Runnable addResponseCookieTask = () ->{
                //如果等于空，说明是第一次加入进来的,为cookie设置对应的值
                uv.set(UUID.fastUUID().toString());
                Cookie uvCookie = new Cookie("uv", uv.get());
                uvCookie.setMaxAge(60*60*24*30);
                uvCookie.setPath(StrUtil.sub(fullShortUrl , fullShortUrl.indexOf("/"),fullShortUrl.length()));
                response.addCookie(uvCookie);
                //设置缓存，初次加入，设置缓存key , TODO： 有一个问题就是 这个key需要设置多长时间，也就是说多长时间内同一个用户访问是属于一个用户
                stringRedisTemplate.opsForSet().add("short-link:stats:uv:" + fullShortUrl, uv.get());
                uvFirstFlag.set(Boolean.TRUE);
            };

            if(ArrayUtil.isNotEmpty(cookies)){
                //cookie不等于空的情况下
                Arrays.stream(cookies)
                        .filter(each -> Objects.equals(each.getName(), "uv"))
                        .findFirst()
                        .map(Cookie::getValue)
                        .ifPresentOrElse(each -> {
                            uv.set(each);
                            //如果不等于空，判断是否曾加入到缓存中（如果是加入过，则加入不进去，因为set保证唯一，返回false）
                            Long uvadded = stringRedisTemplate.opsForSet().add("short-link:stats:uv:" + fullShortUrl, each);
                            uvFirstFlag.set(uvadded != null && uvadded > 0L);

                        //等于空
                        },addResponseCookieTask);

                }else{ //如果cookie第一次就等于空，则认为这个用户当前什么都没有
                addResponseCookieTask.run();
            }
            //获取到用户真实ip地址
            String remoteAddr = LinkUtill.getIpAddr(request);
            Long uipadded = stringRedisTemplate.opsForSet().add("short-link:stats:uip:" + fullShortUrl, remoteAddr);
            uipFirstFlag = uipadded != null && uipadded > 0L;
            //如果传过来的gid是空的 那就再查一次
            if(StrUtil.isBlank(gid)){
                LambdaQueryWrapper<ShortLinkGoToDO> queryWrapper = Wrappers.lambdaQuery(ShortLinkGoToDO.class);
                queryWrapper.eq(ShortLinkGoToDO::getFullShortUrl,fullShortUrl);
                ShortLinkGoToDO shortLinkGoToDO = shortLinkGoToMapper.selectOne(queryWrapper);
                gid = shortLinkGoToDO.getGid();

            }


            //24小时展示
            int hour = DateUtil.hour(new Date(), true);
            Week week = DateUtil.dayOfWeekEnum(new Date());
            //获取当前时间在本月的周几
            int weekValue = week.getValue() - 1;
            LinkAccessStatsDO linkAccessStatsDO = LinkAccessStatsDO.builder()
                    .pv(1)
                    .uv(uvFirstFlag.get() ? 1 : 0)
                    .uip(uipFirstFlag  ? 1 : 0)
                    .hour(hour)
                    .weekday(weekValue)
                    .fullShortUrl(fullShortUrl)
                    .gid(gid)
                    .date(new Date())
                    .build();
            linkAccessStatsMapper.shortLinkStatus(linkAccessStatsDO);
            //高德地图通过ip和key获取到当前用户访问的ip所代表的地区
            Map<String , Object> localParamMap = new HashMap<>();
            localParamMap.put("key",LocalAmpKey);
            localParamMap.put("ip",remoteAddr);
            String localResultStr = HttpUtil.get(AMP_REMOTE_URL, localParamMap);
            //这个就是通过高德的调用网站和key，拿到的对象 localResultObj
            JSONObject localResultObj = JSON.parseObject(localResultStr);
            String infocode = localResultObj.getString("infocode");
            LinkLocaleStatsDO linkLocaleStatsDO;
            //infocode 状态码，如果不为空 ，且为10000 表示获取成功
            if(StrUtil.isNotBlank(infocode) && StrUtil.equals(infocode,"10000")){
                //如果省为空 , 表示 city为空， adcode(城市编码)：为空
                String province = localResultObj.getString("province");

                boolean unknownFlag = StrUtil.equals(province,"[]"); //判断是否为空 ，为true就是空

                linkLocaleStatsDO = LinkLocaleStatsDO.builder()
                        .fullShortUrl(fullShortUrl)
                        .province(unknownFlag ? "未知" : province)
                        .city(unknownFlag ? "未知" : localResultObj.getString("city"))
                        .adcode(unknownFlag ? "未知" : localResultObj.getString("adcode"))
                        //默认是中国
                        .country("中国")
                        .cnt(1)
                        .gid(gid)
                        .date(new Date())
                        .build();
                linkLocaleStatsMapper.shortLocalState(linkLocaleStatsDO);
            }
            //获取到操作系统名称
            String OsName = LinkUtill.UserAgentParser(request);


            LinkOsStatsDO linkOsStatsDO = LinkOsStatsDO.builder()
                    .cnt(1)
                    .gid(gid)
                    .date(new Date())
                    .fullShortUrl(fullShortUrl)
                    .Os(OsName)
                    .build();
            linkOsStatsMapper.shortLocalState(linkOsStatsDO);

            //插入用户浏览器的统计信息
            String browser = LinkUtill.getBrowser(request);
            LinkbrowserStatsDO linkbrowserStatsDO = LinkbrowserStatsDO.builder()
                    .cnt(1)
                    .gid(gid)
                    .date(new Date())
                    .fullShortUrl(fullShortUrl)
                    .browser(browser)
                    .build();
            linkbrowserStatsMapper.shortbrowserState(linkbrowserStatsDO);

            LinkAccessLogsDO linkAccessLogsDO = LinkAccessLogsDO.builder()
                    .ip(remoteAddr)
                    .browser(browser)
                    .os(OsName)
                    .gid(gid)
                    .user(uv.get())
                    .fullShortUrl(fullShortUrl)
                    .build();

            linkAccessLogsMapper.shortLinkAccessLogs(linkAccessLogsDO);

            LinkDeviceStatsDO linkDeviceStatsDO = LinkDeviceStatsDO.builder()
                    .device(LinkUtill.getDevice((request)))
                    .cnt(1)
                    .gid(gid)
                    .fullShortUrl(fullShortUrl)
                    .date(new Date())
                    .build();
            linkDeviceStatsMapper.shortLinkDeviceState(linkDeviceStatsDO);

            LinkNetworkStatsDO linkNetworkStatsDO = LinkNetworkStatsDO.builder()
                    .network(LinkUtill.getNetwork((request)))
                    .cnt(1)
                    .gid(gid)
                    .fullShortUrl(fullShortUrl)
                    .date(new Date())
                    .build();
            linkNetworkStatsMapper.shortLinkNetworkState(linkNetworkStatsDO);


        }catch (Throwable ex){
            log.error("短链接访问量统计异常",ex);
        }


    }

    @Override
    public List<ShortLinkGroupCountQueryRespDTO> ShortLinkCount(List<String> requestParam) {

        QueryWrapper<ShortLinkDO> queryWrapper = Wrappers.query(new ShortLinkDO())
                .select("gid as gid , count(*) as shortLinkCount")
                .in("gid", requestParam)
                .eq("enable_status", 0)
                .groupBy("gid");
        List<Map<String, Object>> shortgroupList = baseMapper.selectMaps(queryWrapper);
        return BeanUtil.copyToList(shortgroupList,ShortLinkGroupCountQueryRespDTO.class);

    }





    public String generateSuffix(ShortLinkCreateReqDTO requestParam) {
        int count = 0;
        String shortUri;
        while (true) {
            if (count > 10) {
                throw new ServiceException("短链接频繁生成，请稍后重试");
            }
            String originUrl = requestParam.getOriginUrl();
            //如果这里生成重复的话，还是继续使用这个原链接继续去生成，那么发生冲突概率会很大
            //所以我们将原链接+当前毫秒数,降低冲突发生概率
            originUrl += System.currentTimeMillis();
            shortUri = HashUtil.hashToBase62(originUrl);

            if (!ShortURICachePenetrationBloomFilter.contains(requestParam.getDomain() + "/" + shortUri)) {
                break;
            }
            count++;
        }
        return shortUri;
    }

    @SneakyThrows
    private String getFavicon(String url){
        URL targetUrl = new URL(url);
        HttpURLConnection connection = (HttpURLConnection) targetUrl.openConnection();
        connection.setRequestMethod("GET");
        connection.connect();
        int responseCode = connection.getResponseCode();
        if(responseCode == HttpURLConnection.HTTP_OK){
            Document document = Jsoup.connect(url).get();
            Element faviconLink = document.select("link[rel~=(?i)^(shortcut)?icon]").first();
            if(faviconLink != null){
                return faviconLink.attr("abs:href");
            }
        }
        return null;
    }

















}

